// call 第一个参数是要指向的对象，第二个参数是个列表
// apply 第一个参数是要指向的对象，第二个参数是个数组
// bind 方法传入的参数为要指向的对象，然后返回一个函数， 然后执行该函数


var foo = {
  value: 3
}
function bar(name, age) {
  console.log(this.value, name, age)
}


var foo1 = {
  value: 3,
  bar: function (name, age) {
    console.log(this.value, name, age)
  }
}
// foo.fn = bar
// foo.fn()
Function.prototype.call2 = function (context) {
  const ctx = context || window // 考虑传入的对象为null

  let arg = []
  for (let i = 1; i < arguments.length; i++) {
    const value = arguments[i]
    arg.push(value)
  }

  ctx.fn = this// 赋值，让调用的对象成为当前对象的属性
  ctx.fn(...arg) // 执行
  delete ctx.fn  // 用完之后在删除fn属性
}
bar.call2(foo, '张三')

/// apply--------------------------------------------------------------
Function.prototype.apply2 = function (context) {
  const ctx = context || window // 考虑传入的对象为null

  let arg = []
  for (let i = 1; i < arguments.length; i++) {
    const value = arguments[i]
    arg = value
  }
  console.log('arg', arg)
  ctx.fn = this
  ctx.fn(...arg)
  delete ctx.fn  // 用完之后在删除fn属性
}

bar.apply2(foo, ['张三', 20])

/// bind --------------------------------------------------------------

Function.prototype.bind2 = function (context) {
  const ctx = context || window // 考虑传入的对象为null

  return (...args) => {
    this.call(ctx, ...args)
  }
}

const bindFn = bar.bind2(foo)
bindFn('张三')